#pragma once
#ifndef ADD_H_
#define ADD_H_

#include "stream_tools.h"

template<
	unsigned N_IO,		// # of IO
	unsigned N_CH,		// # of CHannel
	unsigned VEC_LEN	// ROW * COL
>
void add(
	data_stream<N_IO * BIT_ACTV>& in1,
	data_stream<N_IO * BIT_ACTV>& in2,
	data_stream<N_IO * BIT_ACTV>& out
)
{
	static_assert(N_CH >= N_IO, "add");
	static_assert(N_CH % N_IO == 0, "add");
	constexpr unsigned FOLD = N_CH / N_IO;
	constexpr unsigned ITERS = VEC_LEN;

	assert(in1.size() == VEC_LEN*FOLD);
    assert(in2.size() == VEC_LEN*FOLD);
	assert(out.empty());

	#pragma HLS DATAFLOW
	ap_int<BIT_ACTV> acc[N_IO];
	for (unsigned it = 0; it < ITERS; ++it)
	{
		for (unsigned f = 0; f < FOLD; ++f)
		{
			#pragma HLS PIPELINE II=1
			// load
			ap_uint<N_IO * BIT_ACTV> in_buf1 = in1.read();
			ap_uint<N_IO * BIT_ACTV> in_buf2 = in2.read();
			ap_uint<N_IO * BIT_ACTV> out_buf;
			// calc
			for (unsigned i = 0; i < N_IO; ++i)
			{
				#pragma HLS UNROLL
				ap_int<BIT_ACTV> x1 = in_buf1(SLICE(BIT_ACTV, i));
				ap_int<BIT_ACTV> x2 = in_buf2(SLICE(BIT_ACTV, i));
				acc[i] = x1 + x2;
				out_buf(SLICE(BIT_ACTV, i)) = acc[i];
			}
			out.write(out_buf);
		}
	}

	assert(in1.empty());
    assert(in2.empty());
	assert(out.size() == VEC_LEN*FOLD);
	return;
}
#endif